package me.lwn.auth.config;

import me.lwn.auth.security.config.annotation.authentication.configuration.InitializeSmsUserDetailsBeanManagerConfigurer;
import me.lwn.auth.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import me.lwn.auth.security.provisioning.CustomizeJdbcUserDetailsManager;
import me.lwn.auth.security.web.authentication.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.sql.DataSource;
import java.util.Arrays;

import static me.lwn.auth.security.utils.UserDetailsSqlConstants.USERS_BY_USERNAME_QUERY;

/**
 * @author lizhichao
 * @since 0.0.1
 */
@EnableWebSecurity
public class SecurityConfig {

    private final LoginLockedSupport loginLockedSupport;

    public SecurityConfig(LoginLockedSupport loginLockedSupport) {
        this.loginLockedSupport = loginLockedSupport;
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {

        //WEB登录过滤器，用于支持用户名密码登录和手机号验证码登录方式
        DelegatingLoginAuthenticationFilter filter = new DelegatingLoginAuthenticationFilter();
        filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error"));
        filter.setAuthenticationConverter(new DelegatingAuthenticationConverter(Arrays.asList(
                new UsernamePasswordAuthenticationConverter(),
                new SmsAuthenticationConverter()
        )));
        filter.setLoginLocked(loginLockedSupport);

        http
                .authorizeRequests()
                .antMatchers("/oauth2/captcha", "/sms/**", "/password/forget").permitAll()
                .antMatchers("/**/bootstrap/**", "/**/js/**", "/**/image/**").permitAll()
                .anyRequest().authenticated().and()
                .formLogin().loginPage("/login").permitAll();
        http.addFilterBefore(new CaptchaEndpointFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class);
        http.cors().disable().csrf().disable();

        http.oauth2ResourceServer().jwt();
        SecurityFilterChain chain = http.build();

        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
        filter.setAuthenticationManager(authenticationManager);
        return chain;
    }

    @Bean
    InitializeSmsUserDetailsBeanManagerConfigurer initializeSmsUserDetailsBeanManagerConfigurer(ApplicationContext context) {
        return new InitializeSmsUserDetailsBeanManagerConfigurer(context);
    }

    @Bean
    UserDetailsService userDetailsService(DataSource dataSource) {
        CustomizeJdbcUserDetailsManager userDetailsManager = new CustomizeJdbcUserDetailsManager(dataSource);
        userDetailsManager.setUsersByUsernameQuery(USERS_BY_USERNAME_QUERY);
        return userDetailsManager;
    }
}
